package com.csw.android.player.view.render

import android.content.Context
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.RectF
import android.util.AttributeSet
import android.view.SurfaceView
import android.view.TextureView
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import com.csw.android.player.base.IMediaPlayer
import java.lang.Integer.max
import kotlin.math.min

/**
 * 渲染视图
 * 通过ImageView来实现类似的一个画面缩放效果。
 * 具体思路是通过内部持有ImageView，直接对imageView进行测量以取得其大小作为本视图的大小，然后取得其图片的变换矩阵，将其
 * 图片变幻后的位置及大小应用于渲染视图的测量和布局。
 * 当然，因为渲染视图必定在本视图居中，所以只需设置大小，无需设置位置，主要是我懒得搞了，FIT_START、FIT_END这种东西有人
 * 用？？？
 *
 * 注意，ImageView由于各个android版本实现的不同，会出现各种奇奇怪怪的问题。
 * 比如测量时更新并获取图片变换矩阵，我在查看API31的代码逻辑时并没有问题，但出现了变换矩阵数值与最终效果不一致问题。
 * 经过Drawable bounds的变化断点，发现ImageView在layout、draw的过程中仍可能对变化矩阵和图片边界做调整。
 * 于是我改变策略，当图片的边界发生改变时，我重新提交一次布局更新，来实现尺寸改变。
 * （如果VideoView的大小是不变的，直接对自身measure、layout、postInvalidate应该也是可以的，还不用连累整个视图树重新测量）
 *
 * 注意
 * 1.因为surfaceView是无法通过缩放来实现镜像的，缩放最终改变的只有surface的大小，而画面填充surface时不受其他影响
 * 2.发现部分低分辨率的流在tv上使用textureView进行渲染会绿屏，不知道它底层是什么原因导致的，surfaceView倒是正常
 */
class RenderView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {

    /** 视频宽度（像素） */
    private var videoWidth = 0

    /** 视频高度（像素） */
    private var videoHeight = 0

    /** 经过固定比例调整后的视频宽度（像素） */
    private var ratioVideoWidth = 0

    /** 经过固定比例调整后的视频高度（像素） */
    private var ratioVideoHeight = 0

    /**
     * 缩放类型，同[ImageView.setScaleType]
     */
    var scaleType: ImageView.ScaleType = ImageView.ScaleType.FIT_CENTER
        set(value) {
            if (field == value) {
                return
            }
            field = value
            requestLayout()
        }

    /**
     * 视频宽高比例，用于矫正视频画面与获取的视频实际宽高比不一致问题。
     * 此参数基于视频宽度对视频高度进行修正,小于等于0不做调整。
     */
    var widthHeightRatio: Float = 0f
        set(value) {
            if (field == value) {
                return
            }
            field = value
            applyRatio(videoWidth, videoHeight)
        }

    /**
     * 将变换矩阵应用于渲染视图
     */
    var transformMatrix: Matrix? = null
        set(value) {
            field = value
            postInvalidate()
        }

    /**
     * surface对象变换矩阵
     */
    private var modelMatrix = Matrix()

    /**
     * 镜像
     */
    var mirror: Boolean = false
        set(value) {
            field = value
            postInvalidate()
        }

    private lateinit var realSizeSurfaceView: View

    init {
        //TODO 自定义属性，在布局文件中就决定好要使用什么渲染视图
        initRenderView(false)
    }

    private fun initRenderView(useTextureView: Boolean) {
        if (useTextureView) {
            TextureView(context)
        } else {
            SurfaceView(context)
        }.apply {
            realSizeSurfaceView = this
            addView(this, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
        }
    }

    override fun requestLayout() {
        super.requestLayout()
        //surfaceView的话，会走测量，但分发绘制没有触发，可能是因为视图尺寸没有改变而Surface的画面更新不归视图树管，
        //所以干脆连重新渲染都没走，所以当前是surfaceView的话，有必要补充以下重新绘制
        if (this::realSizeSurfaceView.isInitialized && realSizeSurfaceView is SurfaceView) {
            postInvalidate()
        }
    }

    private val containerRectF = RectF()
    private val displayRectF = RectF()
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        //解析父控件大小，以及renderView的宽高模式
        val parentWidth = MeasureSpec.getSize(widthMeasureSpec)
        val parentHeight = MeasureSpec.getSize(heightMeasureSpec)
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)

        //注意，由于exoplayer对没有尺寸的surface直接不做视频解码，导致播放器无法获取视频宽高，这里至少要指定一个像素的
        // 宽高，同时解决除以0的问题
        val realRenderWidth = max(ratioVideoWidth, 1)
        val realRenderHeight = max(ratioVideoHeight, 1)

        //surfaceView直接固定为渲染宽高
        realSizeSurfaceView.measure(
            MeasureSpec.makeMeasureSpec(realRenderWidth, MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(realRenderHeight, MeasureSpec.EXACTLY),
        )

        //根据宽高模式与视图的缩放比例决定容器大小
        when {
            widthMode == MeasureSpec.EXACTLY -> {
                //宽指定
                val wrapHeight = parentWidth * realRenderHeight / realRenderWidth
                when {
                    heightMode == MeasureSpec.EXACTLY -> {
                        //宽高皆为指定大小，那直接设置为父控件大小
                        setMeasuredDimension(parentWidth, parentHeight)
                    }

                    heightMode == MeasureSpec.AT_MOST && wrapHeight > parentHeight -> {
                        //高度自适应但超过父控件
                        setMeasuredDimension(parentWidth, parentHeight)
                    }

                    else -> {
                        //采用自适应高度
                        setMeasuredDimension(parentWidth, wrapHeight)
                    }
                }
            }

            else -> {
                //宽自适应
                val limitWidth =
                    if (widthMode == MeasureSpec.AT_MOST) parentWidth else Int.MAX_VALUE
                val limitHeight =
                    if (heightMode == MeasureSpec.AT_MOST) parentHeight else Int.MAX_VALUE
                when {
                    heightMode == MeasureSpec.EXACTLY -> {
                        //高度指定
                        val wrapWidth = parentHeight * realRenderWidth / realRenderHeight
                        setMeasuredDimension(min(limitWidth, wrapWidth), parentHeight)
                    }

                    else -> {
                        //高度也自适应
                        when {
                            realRenderWidth <= limitWidth && realRenderHeight <= limitHeight -> {
                                //视频宽高皆在限制之内
                                setMeasuredDimension(realRenderWidth, realRenderHeight)
                            }

                            realRenderWidth > limitHeight && realRenderHeight > limitHeight -> {
                                //视频宽高都超出限制，计算宽高各自需要缩放的系数，取最大缩小倍率
                                val ratioW = realRenderWidth * 1.0 / limitWidth
                                val ratioH = realRenderHeight * 1.0 / limitHeight
                                if (ratioW > ratioH) {
                                    //以宽度为准
                                    setMeasuredDimension(
                                        limitWidth,
                                        limitWidth * realRenderHeight / realRenderWidth
                                    )
                                } else {
                                    //以高度为准
                                    setMeasuredDimension(
                                        limitHeight * realRenderWidth / realRenderHeight,
                                        limitHeight,
                                    )
                                }
                            }

                            realRenderHeight > limitHeight -> {
                                //仅高度超出限制
                                //以高度为准
                                setMeasuredDimension(
                                    limitHeight * realRenderWidth / realRenderHeight,
                                    limitHeight,
                                )
                            }

                            realRenderWidth > limitHeight -> {
                                //仅宽度超出限制
                                //以宽度为准
                                setMeasuredDimension(
                                    limitWidth,
                                    limitWidth * realRenderHeight / realRenderWidth
                                )
                            }
                        }
                    }
                }
            }

        }

        //根据当前容器大小和视频大小计算缩放
        containerRectF.set(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat())
        displayRectF.set(0f, 0f, ratioVideoWidth.toFloat(), ratioVideoHeight.toFloat())

        modelMatrix.reset()
        if (containerRectF.isEmpty || displayRectF.isEmpty) {
            return
        }
        //缩放模式处理
        when (scaleType) {

            ImageView.ScaleType.FIT_XY -> {
                //拉伸填充
                modelMatrix.setRectToRect(displayRectF, containerRectF, Matrix.ScaleToFit.FILL)
            }

            ImageView.ScaleType.CENTER_CROP -> {
                //裁剪填充，将显示区域等比放大到覆盖整个容器，等同于将容器缩小居中至显示区域内部，再反转矩阵
                modelMatrix.setRectToRect(containerRectF, displayRectF, Matrix.ScaleToFit.CENTER)
                modelMatrix.invert(modelMatrix)
            }

            else -> {
                //等比缩放至边界，垂直或水平方向留黑边
                modelMatrix.setRectToRect(displayRectF, containerRectF, Matrix.ScaleToFit.CENTER)
            }
        }
    }

    /**
     * 更新视图大小
     * @param width 视频宽度
     * @param height 视频高度
     */
    fun updateVideoSize(width: Int, height: Int) {
        if (videoWidth == width && videoHeight == height) {
            return
        }
        videoWidth = width
        videoHeight = height
        applyRatio(videoWidth, videoHeight)
    }

    /**
     * 应用视频比例变换
     */
    private fun applyRatio(width: Int, height: Int) {
        if (widthHeightRatio > 0) {
            if (width == ratioVideoWidth && (width / widthHeightRatio).toInt() == ratioVideoHeight) {
                return
            }
            ratioVideoWidth = width
            ratioVideoHeight = (width / widthHeightRatio).toInt()
        } else {
            if (width == ratioVideoWidth && height == ratioVideoHeight) {
                return
            }
            ratioVideoWidth = width
            ratioVideoHeight = height
        }
        requestLayout()
    }

    private var eyeCenterPosition = floatArrayOf(0f, 0f)
    private var matrix = Matrix()

    /**
     * 应用渲染视图变换
     */
    override fun dispatchDraw(canvas: Canvas) {
        if (canvas == null) {
            super.dispatchDraw(canvas)
        } else {
            canvas.save()
            //应用场景变换
            transformMatrix?.let {
                canvas.concat(it)
            }
            //应用surface模型变换
            canvas.concat(modelMatrix)
            if (mirror) {
                //对视角中心，进行逆向变换，得到它在画布中的坐标，在该坐标进行一次左右镜像翻转
                eyeCenterPosition[0] = width / 2f
                eyeCenterPosition[1] = height / 2f
                matrix.reset()
                transformMatrix?.let {
                    matrix.preConcat(it)
                }
                matrix.preConcat(modelMatrix)
                matrix.invert(matrix)
                matrix.mapPoints(eyeCenterPosition)
                canvas.scale(-1f, 1f, eyeCenterPosition[0], eyeCenterPosition[1])
            }
            super.dispatchDraw(canvas)
            canvas.restore()
        }
    }

    /**
     * 设置Player渲染面
     */
    fun settingPlayerSurface(mediaPlayer: IMediaPlayer, bind: Boolean) {
        if (bind) {
            realSizeSurfaceView.let {
                when (it) {
                    is SurfaceView -> {
                        mediaPlayer.setSurfaceView(it)
                    }

                    is TextureView -> {
                        mediaPlayer.setTextureView(it)
                    }

                    else -> {
                        mediaPlayer.setSurfaceView(null)
                    }
                }
            }
        } else {
            mediaPlayer.setSurfaceView(null)
        }
    }

}